package znet

import (
	"fmt"
	"strconv"
	"zinx/utils"
	"zinx/ziface"
)

type MsgHandle struct {
	//存放每个MsgId 所对应的处理方法的map属性
	Apis map[uint32]ziface.IRouter

	//消息队列（管道集合）
	TaskQueue []chan ziface.IRequest

	//worker工作池的数量
	WorkerPoolSize uint32
}

func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		Apis:           make(map[uint32]ziface.IRouter),
		WorkerPoolSize: utils.GlobalObject.WorkerPoolSize,                               //从全局参数配置中获取，也可以在配置文件中让用户配置
		TaskQueue:      make([]chan  ziface.IRequest, utils.GlobalObject.WorkerPoolSize), //这是个切片，没有逗号吗？？？
		//utils.GlobalObject.WorkerPoolSize这里要和上面一致，多少个worker就有多少个消息队列对应
	}
}

//调度/执行对应的Router消息处理方法
func (mh *MsgHandle) DoMsgHandler(request ziface.IRequest) {
	//从Request中找到MsgId
	handler, ok := mh.Apis[request.GetMsgId()]
	if !ok { //看看有没有对应的方法
		fmt.Println("api msgId = ", request.GetMsgId(), " is not FOUND!")
	  	return       //这里一定要return，否则找不到handler还要调用，handler就是调空接口了    
	}
	//根据MsgId调度相应的业务
	handler.PreHandle(request)
	handler.Handle(request)
	handler.PostHandle(request)
}

//为消息添加具体的处理逻辑
func (mh *MsgHandle) AddRouter(msgId uint32, router ziface.IRouter) {
	//1 判断当前msg绑定的API处理方法是否已经存在
	if _, ok := mh.Apis[msgId]; ok { //从map中取id，看是否存在
		panic("repeated api , msgId = " + strconv.Itoa(int(msgId))) //字符串转int：Atoi()   int转字符串: Itoa()
	}
	//2 添加msg与api的绑定关系
	mh.Apis[msgId] = router //就是一个id对应一个路由规则
	fmt.Println("Add api msgId = ", msgId)
}

//启动一个工作池（对外开放）开启一个工作池的动作只能发生一次，一个zinx框架只能有一个worker工作池
func (mh *MsgHandle) StartWorkerPool() {
	for i := 0; i < int(mh.WorkerPoolSize); i++ { // int(mh.WorkerPoolSize)这里要把uint 32转为int
		//第i个worker用第i个channel
		//给当前worker对应的任务队列开辟空间
		mh.TaskQueue[i] = make(chan ziface.IRequest, utils.GlobalObject.MaxWorkerTaskLen)
		//启动当前Worker，阻塞的等待对应的任务队列是否有消息传递进来
		go mh.StartOneWorker(i, mh.TaskQueue[i])

	}

}

//启动一个工作流程
func (mh *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest) {
	fmt.Println("Worker ID = ", workerID, " is started.")
	//不断的阻塞等待消息队列中的消息
	for {
		select {
		//如果有消息过来，出列的就是一个客户端的request，执行当前的request所绑定的任务
		case request := <-taskQueue:
			mh.DoMsgHandler(request) //调用相对应的handler就OK了
		}
	}
}

//现在，worker工作池已经准备就绪了，那么就需要有一个给到worker工作池消息的入口，我们再定义一个方法，将消息交给TaskQueue,由worker进行处理
func (mh *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest) {
	//根据ConnID来分配当前的连接应该由哪个worker负责处理          不能用msgId，因为msgId是客户端的，能不断重复
	//轮询的平均分配法则

	//得到需要处理此条连接的workerID
	workerID := request.GetConnection().GetConnID() % mh.WorkerPoolSize
	fmt.Println("Add ConnID=", request.GetConnection().GetConnID(), " request msgID=", request.GetMsgId(), "to workerID=", workerID)
	//将请求消息发送给任务队列
	mh.TaskQueue[workerID] <- request
}
